# -*- coding: utf-8 -*-


# cmap2owl -- Helper application to convert from concept maps to OWL ontologies
# Copyright (c) 2008-2013  Rodrigo Rizzi Starr
#  
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#  
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#  
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.


'''
Contains a set of rules to identify disjunction relationships

@author: Rodrigo Rizzi Starr
@copyright: Copyright © 2008-2013 Rodrigo Rizzi Starr
@license: MIT License
@contact: rodrigo.starr@gmail.com
'''


import sys

# GTK+ imports
import pygtk
if not sys.platform == 'win32':
    pygtk.require('2.0')
try:
    import gtk
except:
    sys.exit(1)

# Project imports
from CMap2Prolog import assemblyQuery
from mapperRule import MapperRule
from toogleQuestion import ToogleQuestion
from infoProvider import InfoProvider
from log import log
log.debug("Loaded...")
           
            
class DisjunctionRule(MapperRule):
    "Detects when classes are distinct from each other"

    def __init__(self, db):
        
        # Heuristics to identify disjoint classes: 
        #
        # 1. If I know some classes are subclasses of another, then it only
        #    makes sense to consider disjunction among those because, of course,
        #    they are not disjunct with the superclass
        #
        # 2. All classes that are not part of 'subclass groups' (defined on
        #    first rule) are candidates for conjunction or disjunction.
        #
        #
        # 5. 
        rules = [
                 "inferenceGroup(disjunctionRule, [disjunction(_)])",
                 
                 "disjunction(Set) :- "
                    "setof(Subclass, ("
                        "subclassOWL(Subclass, _), "
                        "\+ (isFalse(disjunction(OSet)), "
                            "member(Subclass, OSet))"
                    "), Set),"
                    "length(Set,L), L > 1, "
                    "\+ ( isFalse(disjunction(AnotherSet)), "
                       "subset(Set, AnotherSet))",
                    
                 "disjunction(Set) :- "
                    "setof(C1, (classOWL(C1), ("
                        "\+ subclassOWL(C1,_), "
                        "\+ (isFalse(disjunction(OSet)), member(C1, OSet))"
                    ")), Set),"
                    "length(Set,L), L > 1, "
                    "\+ ( isFalse(disjunction(AnotherSet)), "
                       "subset(Set, AnotherSet))",
                    
                 "disjunctionOWL(Set) :- isTrue(disjunction(Set))",
                ]
         
        MapperRule.__init__(self, "DisjunctionRules", db, rules)
            
    def getQuestions(self):
        "Queries the KB to find possible disjoint sets"
        
        questions = []
        query = ("possible(disjunction(Set)), "
                "\+ (isTrue(disjunction(OtherSet)), "
                     "subset(Set, OtherSet)), "
                "\+ (isFalse(disjunction(AnotherSet)), "
                     "(subset(Set, AnotherSet); "
                      "subset(AnotherSet, Set)))")
                      
        disjunctions = self.db.query(query)
        
        for answer in disjunctions:
            answer["Ids"] = set(answer["Set"])
            questions.append(DisjunctionQuestion(
                    self.db,
                    answer,
                    'Which of the following <b>classes</b> are certainly '
                        'different among themselves ?',
                 helpFile = "allDifferent",
                 startupState = False,
                 buttonLabels = ["Different", "Not different"],
              ))
            
        return questions
      
    def getInfoProvider(self):
        "Return a provider for info about the result of this class"
        
        return InfoProvider(self.db,
                            "isTrue(disjunction(DisjSet)), El = %s,"
                                "member(El, DisjSet),"
                                "subtract(DisjSet, [El], Set)",
                            "This class is different from:")

    
class DisjunctionQuestion(ToogleQuestion):
    "Implements a better way of checking if a disjunction group has changed"
    
    def updateQueryData(self):
        "Creates a string representing a query"
        query = ("possible(disjunction(NewSet)), "
                "(subset(NewSet, %(Set)s); subset(%(Set)s, NewSet)), "
                "length(NewSet, L), L > 1,"
                "\+ ( isTrue(disjunction(OtherSet)), "
                     "subset(%(Set)s, OtherSet)), "
                "\+ ( isFalse(disjunction(AnotherSet)), "
                     "(subset(%(Set)s, AnotherSet); "
                      "subset(AnotherSet, %(Set)s)))")
        
        self.queryData = assemblyQuery(query, self.data)
    
    def updateIds(self):
        "Return a set of updated ids"
        
        if self.buttonPressed != True:
            answer = list(self.db.query(self.queryIds))
            if len(answer) == 0:
                self.ids = set()
            else:
                answer = answer[0]['NewSet']
                self.ids = set(answer)
     
    def assertOnYes(self):
        "Asserts an updated set of elements"
        
        activeIds = self.getActiveIds()
        self.ids = set(activeIds)
        
        queries = [assemblyQuery('isTrue(disjunction(%s))', activeIds)]
        
        if len(activeIds) < len(self.middle):
            # So we know not all options were selected
            # This entails that the group as a whole is not disjoint
            queries.append(assemblyQuery('isFalse(disjunction(%s))', self.ids))
            
        return queries
    
    def assertOnNo(self):
        "Creates a string representing a question"
        
        activeIds = self.getActiveIds()
                
        query = assemblyQuery('isFalse(disjunction(%s))', activeIds)
        return [query]
       
    def __eq__(self, element):
        if type(self) != type(element):
            return False
        
        return self.ids == element.ids
    
